class Store {
  _modules: any
  _vm: {
    _data: {
      $$state: any
    }
  }
  _mutations: any
  _subscribers: any
  _actions: any
  _actionSubscribers: any
  $state: any
  _watcherVM: any
  getters: any
  _committing: any
  /**
   * 初始化 option
   * @param options 
   */
  constructor(options: { plugins?: any[]; strict?: false; }) {

    const { plugins = [], strict = false } = options
    const store = this
    const { dispatch, commit } = this

    this.dispatch = function boundDispatch(type, payload) {
      return dispatch.call(store, type, payload)
    }

    this.commit = function boundCommit(type, payload, options) {
      return commit.call(store, type, payload, options)
    }
    const state = this._modules.root.state
  }

  get state() {
    return this.$state
  }

  set state(v) {

  }

  commit(type: string, payload: any, options: any) {
    const mutation = { type, payload }
    const entry = this._mutations[type]
    this._withCommit(() => {
      entry.forEach(function commitIterator(handler: Function) {
        handler(payload)
      })
    })

    this._subscribers
      .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
      .forEach((sub: Function) => sub(mutation, this.state))
  }

  dispatch(type: string, payload: Function) {
    const action = { type, payload }
    const entry = this._actions[type]
    try {
      this._actionSubscribers
        .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
        .filter((sub: { before: any }) => sub.before)
        .forEach((sub: { before: (arg0: { type: string; payload: Function }, arg1: any) => any }) => sub.before(action, this.state))
    } catch (e) {

    }
    const result: Promise<any> = entry.length > 1
      ? Promise.all(entry.map((handler: (arg0: Function) => any) => handler(payload)))
      : entry[0](payload)

    return new Promise((resolve, reject) => {
      result.then(res => {
        try {
          this._actionSubscribers
            .filter((sub: { after: any }) => sub.after)
            .forEach((sub: { after: (arg0: { type: string; payload: Function }, arg1: any) => any }) => sub.after(action, this.state))
        } catch (e) {

        }
        resolve(res)
      }, error => {
        try {
          this._actionSubscribers
            .filter((sub: { error: any }) => sub.error)
            .forEach((sub: { error: (arg0: { type: string; payload: Function }, arg1: any, arg2: any) => any }) => sub.error(action, this.state, error))
        } catch (e) {

        }
        reject(error)
      })
    })
  }

  subscribe(fn: { before: Function }, options: any) {
    return genericSubscribe(fn, this._subscribers, options)
  }

  subscribeAction(fn: Function, options: any) {
    const subs = typeof fn === 'function' ? { before: fn } : fn
    return genericSubscribe(subs, this._actionSubscribers, options)
  }

  watch(getter: (arg0: any, arg1: any) => any, cb: any, options: any) {
    return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)
  }
  replaceState(state: any) {
    this._withCommit(() => {
      this._vm._data.$$state = state
    })
  }

  // registerModule (path, rawModule, options = {}) {
  //   if (typeof path === 'string') path = [path]

  //   if (__DEV__) {
  //     assert(Array.isArray(path), `module path must be a string or an Array.`)
  //     assert(path.length > 0, 'cannot register the root module by using registerModule.')
  //   }

  //   this._modules.register(path, rawModule)
  //   installModule(this, this.state, path, this._modules.get(path), options.preserveState)
  //   // reset store to update getters...
  //   resetStoreVM(this, this.state)
  // }



  _withCommit(fn: () => void) {
    const committing = this._committing
    this._committing = true
    fn()
    this._committing = committing
  }




}

function genericSubscribe(fn: { before: Function }, subs: { before: Function }[], options: any) {
  if (subs.indexOf(fn) < 0) {
    options && options.prepend ? subs.unshift(fn) : subs.push(fn)
  }
  return () => {
    const i = subs.indexOf(fn)
    if (i > -1) subs.splice(i, 1)
  }
}

function registerAction (store: { _actions: { [x: string]: any[] }; getters: any; state: any; _devtoolHook: { emit: (arg0: string, arg1: any) => void } }, type: string | number, handler: { call: (arg0: any, arg1: { dispatch: any; commit: any; getters: any; state: any; rootGetters: any; rootState: any }, arg2: any) => any }, local: { dispatch: any; commit: any; getters: any; state: any }) {
  const entry = store._actions[type] || (store._actions[type] = [])
  entry.push(function wrappedActionHandler (payload: any) {
    let res = handler.call(store, {
      dispatch: local.dispatch,
      commit: local.commit,
      getters: local.getters,
      state: local.state,
      rootGetters: store.getters,
      rootState: store.state
    }, payload)
    // if (!isPromise(res)) {
    //   res = Promise.resolve(res)
    // }
    if (store._devtoolHook) {
      return res.catch((err: any) => {
        store._devtoolHook.emit('vuex:error', err)
        throw err
      })
    } else {
      return res
    }
  })
}
